<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图片分辨率调整与压缩工具</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#3B82F6',
                        secondary: '#10B981',
                        accent: '#F59E0B',
                        neutral: '#1F2937',
                        light: '#F3F4F6'
                    },
                    fontFamily: {
                        sans: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .transition-custom {
                transition: all 0.3s ease;
            }
            .drop-shadow-custom {
                filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1));
            }
        }
    </style>
</head>
<body class="bg-gray-50 min-h-screen font-sans text-neutral">
    <div class="container mx-auto px-4 py-8 max-w-6xl">
        <!-- 标题区域 -->
        <header class="text-center mb-10">
            <h1 class="text-[clamp(1.8rem,4vw,2.8rem)] font-bold text-neutral mb-3">
                <i class="fa fa-picture-o text-primary mr-2"></i>图片分辨率调整与压缩工具
            </h1>
            <p class="text-gray-600 max-w-2xl mx-auto">
                上传图片，指定目标分辨率和文件大小，工具将自动调整并压缩图片至最佳质量
            </p>
        </header>

        <main class="grid md:grid-cols-2 gap-8">
            <!-- 左侧：上传和设置区域 -->
            <div class="bg-white rounded-xl shadow-lg p-6 transform hover:shadow-xl transition-custom">
                <!-- 上传区域 -->
                <div id="dropArea" class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center mb-8 cursor-pointer hover:border-primary transition-custom">
                    <i class="fa fa-cloud-upload text-5xl text-gray-400 mb-4"></i>
                    <p class="text-gray-600 mb-2">拖放图片到此处，或点击上传</p>
                    <p class="text-gray-400 text-sm">支持 JPG、PNG 格式</p>
                    <input type="file" id="fileInput" accept="image/jpeg, image/png" class="hidden">
                </div>

                <!-- 设置区域 -->
                <div id="settingsArea" class="hidden">
                    <h2 class="text-xl font-semibold mb-5 flex items-center">
                        <i class="fa fa-sliders text-primary mr-2"></i>处理设置
                    </h2>
                    
                    <!-- 分辨率设置 -->
                    <div class="mb-6">
                        <label class="block text-gray-700 mb-2 font-medium">目标分辨率（像素）</label>
                        <div class="grid grid-cols-2 gap-4">
                            <div>
                                <label class="block text-sm text-gray-500 mb-1">宽度</label>
                                <input type="number" id="targetWidth" min="1" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" placeholder="宽度">
                            </div>
                            <div>
                                <label class="block text-sm text-gray-500 mb-1">高度</label>
                                <input type="number" id="targetHeight" min="1" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" placeholder="高度">
                            </div>
                        </div>
                    </div>

                    <!-- 目标大小设置 -->
                    <div class="mb-6">
                        <label class="block text-gray-700 mb-2 font-medium">目标文件大小不超过</label>
                        <div class="flex items-center">
                            <input type="number" id="targetSize" min="1" step="10" class="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" placeholder="大小">
                            <span class="ml-2 text-gray-600">KB</span>
                        </div>
                    </div>

                    <!-- 处理按钮 -->
                    <button id="processBtn" class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-3 px-4 rounded-md transition-custom flex items-center justify-center">
                        <i class="fa fa-cogs mr-2"></i>处理图片
                    </button>
                </div>
            </div>

            <!-- 右侧：预览和结果区域 -->
            <div class="bg-white rounded-xl shadow-lg p-6 transform hover:shadow-xl transition-custom">
                <h2 class="text-xl font-semibold mb-5 flex items-center">
                    <i class="fa fa-eye text-primary mr-2"></i>图片预览
                </h2>
                
                <!-- 原始图片预览 -->
                <div id="originalPreview" class="mb-8 hidden">
                    <h3 class="text-lg font-medium mb-2 text-gray-700">原始图片</h3>
                    <div class="bg-gray-100 rounded-lg p-4 flex justify-center items-center min-h-[150px]">
                        <img id="originalImage" class="max-w-full max-h-[200px] object-contain drop-shadow-custom" alt="原始图片">
                    </div>
                    <div class="mt-2 text-sm text-gray-500">
                        <span id="originalDimensions">原始尺寸: -- × -- 像素</span> | 
                        <span id="originalSize">大小: -- KB</span>
                    </div>
                </div>
                
                <!-- 处理后图片预览 -->
                <div id="processedPreview" class="mb-8 hidden">
                    <h3 class="text-lg font-medium mb-2 text-gray-700">处理后图片</h3>
                    <div class="bg-gray-100 rounded-lg p-4 flex justify-center items-center min-h-[150px]">
                        <img id="processedImage" class="max-w-full max-h-[200px] object-contain drop-shadow-custom" alt="处理后图片">
                    </div>
                    <div class="mt-2 text-sm text-gray-500">
                        <span id="processedDimensions">尺寸: -- × -- 像素</span> | 
                        <span id="processedSize">大小: -- KB</span> |
                        <span id="compressionQuality">质量: --%</span>
                    </div>
                </div>
                
                <!-- 下载按钮 -->
                <button id="downloadBtn" class="w-full bg-secondary hover:bg-secondary/90 text-white font-medium py-3 px-4 rounded-md transition-custom flex items-center justify-center hidden">
                    <i class="fa fa-download mr-2"></i>下载图片
                </button>
                
                <!-- 处理状态 -->
                <div id="statusArea" class="text-center py-8 hidden">
                    <div id="loadingIndicator" class="hidden">
                        <div class="inline-block animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-primary mb-4"></div>
                        <p class="text-gray-600">正在处理图片...</p>
                    </div>
                    <div id="errorMessage" class="text-red-500 hidden">
                        <i class="fa fa-exclamation-circle text-xl mb-2"></i>
                        <p>处理图片时出错，请重试</p>
                    </div>
                </div>
            </div>
        </main>

        <!-- 说明区域 -->
        <section class="mt-12 bg-white rounded-xl shadow-lg p-6">
            <h2 class="text-xl font-semibold mb-4 flex items-center">
                <i class="fa fa-info-circle text-primary mr-2"></i>使用说明
            </h2>
            <ul class="list-disc pl-5 text-gray-600 space-y-2">
                <li>上传JPG或PNG格式的图片，支持多次重新上传</li>
                <li>工具会显示图片的原始尺寸（而非预览尺寸）和大小</li>
                <li>设置目标分辨率（宽度和高度，单位：像素），默认值为260×320像素</li>
                <li>设置目标文件大小（不超过指定KB），默认值为60KB</li>
                <li>点击"处理图片"按钮开始处理</li>
                <li>处理完成后可以预览并下载结果图片</li>
                <li>图片处理逻辑：先按比例缩放，无法按比例匹配时进行裁剪，然后压缩至不超过指定大小</li>
            </ul>
        </section>

        <footer class="mt-10 text-center text-gray-500 text-sm">
            <p>图片处理工具 &copy; 2023 - 所有处理均在本地完成，不会上传您的图片</p>
        </footer>
    </div>

    <script>
        // 获取DOM元素
        const dropArea = document.getElementById('dropArea');
        const fileInput = document.getElementById('fileInput');
        const settingsArea = document.getElementById('settingsArea');
        const originalPreview = document.getElementById('originalPreview');
        const originalImage = document.getElementById('originalImage');
        const originalDimensions = document.getElementById('originalDimensions');
        const originalSize = document.getElementById('originalSize');
        const processedPreview = document.getElementById('processedPreview');
        const processedImage = document.getElementById('processedImage');
        const processedDimensions = document.getElementById('processedDimensions');
        const processedSize = document.getElementById('processedSize');
        const compressionQuality = document.getElementById('compressionQuality');
        const targetWidth = document.getElementById('targetWidth');
        const targetHeight = document.getElementById('targetHeight');
        const targetSize = document.getElementById('targetSize');
        const processBtn = document.getElementById('processBtn');
        const downloadBtn = document.getElementById('downloadBtn');
        const statusArea = document.getElementById('statusArea');
        const loadingIndicator = document.getElementById('loadingIndicator');
        const errorMessage = document.getElementById('errorMessage');
        
        let originalImg = null;
        let processedImgDataUrl = null;
        
        // 拖放事件处理
        ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
            dropArea.addEventListener(eventName, preventDefaults, false);
        });
        
        function preventDefaults(e) {
            e.preventDefault();
            e.stopPropagation();
        }
        
        ['dragenter', 'dragover'].forEach(eventName => {
            dropArea.addEventListener(eventName, highlight, false);
        });
        
        ['dragleave', 'drop'].forEach(eventName => {
            dropArea.addEventListener(eventName, unhighlight, false);
        });
        
        function highlight() {
            dropArea.classList.add('border-primary', 'bg-blue-50');
        }
        
        function unhighlight() {
            dropArea.classList.remove('border-primary', 'bg-blue-50');
        }
        
        dropArea.addEventListener('drop', handleDrop, false);
        
        function handleDrop(e) {
            const dt = e.dataTransfer;
            const file = dt.files[0];
            handleFiles(file);
        }
        
        // 点击上传
        dropArea.addEventListener('click', () => {
            // 重置文件输入值，允许重新上传相同文件
            fileInput.value = '';
            fileInput.click();
        });
        
        fileInput.addEventListener('change', () => {
            if (fileInput.files.length > 0) {
                handleFiles(fileInput.files[0]);
            }
        });
        
        // 处理上传的文件
        function handleFiles(file) {
            if (!file.type.match('image/jpeg|image/png')) {
                alert('请上传JPG或PNG格式的图片');
                return;
            }
            
            // 重置之前的预览和处理结果
            resetPreviews();
            
            // 显示文件信息
            const fileSizeKB = (file.size / 1024).toFixed(1);
            
            // 预览原始图片
            const reader = new FileReader();
            reader.onload = function(e) {
                // 创建一个临时图像对象来获取原始尺寸
                const tempImg = new Image();
                tempImg.onload = function() {
                    // 使用naturalWidth和naturalHeight确保获取的是图片原始尺寸
                    const originalWidth = this.naturalWidth;
                    const originalHeight = this.naturalHeight;
                    
                    // 设置预览图片
                    originalImage.src = e.target.result;
                    
                    // 显示原始图片信息，明确标注为原始尺寸
                    originalDimensions.textContent = `原始尺寸: ${originalWidth} × ${originalHeight} 像素`;
                    originalSize.textContent = `大小: ${fileSizeKB} KB`;
                    originalPreview.classList.remove('hidden');
                    
                    // 保存原始图片对象
                    originalImg = new Image();
                    originalImg.src = e.target.result;
                    
                    // 设置默认目标分辨率为260×320像素
                    targetWidth.value = 260;
                    targetHeight.value = 320;
                    
                    // 设置默认目标大小为60KB
                    targetSize.value = 60;
                    
                    // 显示设置区域
                    settingsArea.classList.remove('hidden');
                };
                tempImg.src = e.target.result;
            };
            reader.readAsDataURL(file);
        }
        
        // 重置预览区域
        function resetPreviews() {
            // 清除图片源，避免缓存问题
            originalImage.src = '';
            processedImage.src = '';
            
            // 重置状态变量
            originalImg = null;
            processedImgDataUrl = null;
            
            // 隐藏预览和结果区域
            processedPreview.classList.add('hidden');
            downloadBtn.classList.add('hidden');
            statusArea.classList.add('hidden');
            loadingIndicator.classList.add('hidden');
            errorMessage.classList.add('hidden');
        }
        
        // 处理图片按钮点击
        processBtn.addEventListener('click', processImage);
        
        // 处理图片
        function processImage() {
            if (!originalImg) return;
            
            // 验证输入
            const width = parseInt(targetWidth.value);
            const height = parseInt(targetHeight.value);
            const sizeKB = parseInt(targetSize.value);
            
            if (isNaN(width) || isNaN(height) || isNaN(sizeKB) || width <= 0 || height <= 0 || sizeKB <= 0) {
                alert('请输入有效的数值');
                return;
            }
            
            // 显示处理状态
            showLoading();
            
            // 使用setTimeout模拟异步处理，避免UI阻塞
            setTimeout(() => {
                try {
                    // 调整分辨率（缩放和裁剪）
                    const resizedCanvas = resizeAndCropImage(originalImg, width, height);
                    
                    // 压缩到指定大小
                    compressToTargetSize(resizedCanvas, sizeKB, (dataUrl, info) => {
                        // 显示处理结果
                        processedImage.src = dataUrl;
                        processedImgDataUrl = dataUrl;
                        
                        processedDimensions.textContent = `尺寸: ${info.width} × ${info.height} 像素`;
                        processedSize.textContent = `大小: ${info.sizeKB.toFixed(1)} KB`;
                        compressionQuality.textContent = `质量: ${info.quality.toFixed(0)}%`;
                        
                        processedPreview.classList.remove('hidden');
                        downloadBtn.classList.remove('hidden');
                        hideStatus();
                    });
                } catch (error) {
                    console.error('图片处理错误:', error);
                    showError();
                }
            }, 100);
        }
        
        // 调整分辨率：先按比例缩放，再裁剪
        function resizeAndCropImage(img, targetW, targetH) {
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            
            // 计算原始宽高比和目标宽高比
            const originalRatio = img.naturalWidth / img.naturalHeight;
            const targetRatio = targetW / targetH;
            
            let scaledW, scaledH;
            
            // 根据比例决定缩放方式
            if (originalRatio > targetRatio) {
                // 原始图片更宽，按高度缩放，然后裁剪宽度
                scaledH = targetH;
                scaledW = img.naturalWidth * (targetH / img.naturalHeight);
            } else {
                // 原始图片更高，按宽度缩放，然后裁剪高度
                scaledW = targetW;
                scaledH = img.naturalHeight * (targetW / img.naturalWidth);
            }
            
            // 设置canvas尺寸为目标尺寸
            canvas.width = targetW;
            canvas.height = targetH;
            
            // 计算裁剪位置
            const offsetX = (scaledW > targetW) ? (scaledW - targetW) / 2 : 0;
            const offsetY = (scaledH > targetH) ? (scaledH - targetH) / 2 : 0;
            
            // 绘制并裁剪图片
            ctx.drawImage(
                img,
                0, 0, img.naturalWidth, img.naturalHeight,
                -offsetX, -offsetY, scaledW, scaledH
            );
            
            return canvas;
        }
        
        // 压缩图片到目标大小
        function compressToTargetSize(canvas, targetSizeKB, callback) {
            // 目标大小转换为字节
            const targetSizeBytes = targetSizeKB * 1024;
            
            // 二分法查找最佳质量
            let minQuality = 0.1;  // 最低质量
            let maxQuality = 1.0;  // 最高质量
            let bestQuality = 0.8; // 初始质量
            let bestDataUrl = null;
            let bestInfo = null;
            let iterations = 0;
            const maxIterations = 15; // 最大迭代次数
            
            function testQuality(quality) {
                iterations++;
                
                // 将canvas转换为jpeg
                const dataUrl = canvas.toDataURL('image/jpeg', quality);
                
                // 计算数据URL的大小（大约）
                // 数据URL的大小 ≈ (base64字符串长度 * 3) / 4
                const sizeBytes = Math.round((dataUrl.length * 3) / 4);
                const sizeKB = sizeBytes / 1024;
                
                // 存储当前信息
                const info = {
                    width: canvas.width,
                    height: canvas.height,
                    sizeKB: sizeKB,
                    quality: quality * 100
                };
                
                // 如果找到合适的质量或达到最大迭代次数，返回结果
                if (sizeBytes <= targetSizeBytes || iterations >= maxIterations) {
                    // 如果这是最佳结果，使用它
                    if (!bestDataUrl || sizeBytes > bestInfo.sizeKB * 1024) {
                        bestDataUrl = dataUrl;
                        bestInfo = info;
                    }
                    callback(bestDataUrl, bestInfo);
                    return;
                }
                
                // 如果当前质量下的大小仍然太大，降低最大质量
                if (sizeBytes > targetSizeBytes) {
                    maxQuality = quality;
                    // 如果这是目前找到的最佳结果，保存它
                    if (!bestDataUrl || quality > bestInfo.quality / 100) {
                        bestDataUrl = dataUrl;
                        bestInfo = info;
                    }
                    // 尝试更低的质量
                    testQuality((minQuality + maxQuality) / 2);
                } else {
                    // 如果当前质量下的大小足够小，提高最小质量
                    minQuality = quality;
                    // 保存这个更好的结果
                    bestDataUrl = dataUrl;
                    bestInfo = info;
                    // 尝试更高的质量
                    testQuality((minQuality + maxQuality) / 2);
                }
            }
            
            // 开始测试
            testQuality(bestQuality);
        }
        
        // 下载图片
        downloadBtn.addEventListener('click', () => {
            if (!processedImgDataUrl) return;
            
            const link = document.createElement('a');
            link.download = 'processed-image.jpg';
            link.href = processedImgDataUrl;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        });
        
        // 状态显示函数
        function showLoading() {
            processedPreview.classList.add('hidden');
            downloadBtn.classList.add('hidden');
            statusArea.classList.remove('hidden');
            loadingIndicator.classList.remove('hidden');
            errorMessage.classList.add('hidden');
        }
        
        function showError() {
            loadingIndicator.classList.add('hidden');
            errorMessage.classList.remove('hidden');
        }
        
        function hideStatus() {
            statusArea.classList.add('hidden');
            loadingIndicator.classList.add('hidden');
            errorMessage.classList.add('hidden');
        }
    </script>
</body>
</html>
